# 开发人员：NiceToMeeetU
# 项目名称：Kinship_Calculation
# 文件名称：kinship_calculation01，py
# 开发日期：19/10/30 09：58
import re
import os

# 首先声明全局变量，十个基本称呼及基本关系代号
call_list = ['女儿', '儿子', '丈夫', '妻子', '哥哥', '弟弟', '姐姐', '妹妹', '爸爸', '妈妈']
code_list = ['N', 'Z', 'F', 'Q', 'G', 'D', 'J', 'S', 'B', 'M']


def input_check(str_in):
    """
    正则表达式检查输入是否合法
    :param str_in: 中文字符串输入
    :return bool: 返回是否满足输入要求的逻辑值
    """
    correct_enter = re.compile('^([女儿|儿子|丈夫|妻子|老公|老婆|哥哥|弟弟|姐姐|妹妹|爸爸|妈妈][的]?)*$')
    return correct_enter.match(str_in)


def input_handle(str_in):
    """
    中文输入的基本分词
    :param str_in: 符合输入要求的中文字符串
    :return seq: 返回中文字符串对应的完整代号序列字符串
    """
    # 补充几个常用的称呼提升输入多样性
    call_addition1 = ['老公', '老婆','']
    call_addition2 = ['F', 'Q','']
    call_dic = dict(zip(call_list + call_addition1, code_list + call_addition2))  # 中文转换识别字典
    str_in_split = str_in.strip().split("的")  # 简单粗暴的按“的”分词
    return ''.join([call_dic[i] for i in str_in_split])


def gender_check(seq_in):
    """
    性别判定
    :param seq_in:
    :return: 返回各个代号的性别，1男，0女
    """
    gender_dic = dict(zip(code_list, [0, 1, 1, 0, 1, 1, 0, 0, 1, 0]))
    return gender_dic[seq_in[-1]]


def seq_simplify(seq_in):
    """
    @@关键算法@@，替换序列中48种无效或重复的代号组合，防止出现自递归迭代
    :param seq_in:完整序列
    :return:返回已处理好的有效代号组合
    """
    # 可直接替换的32组可折叠代号组合
    folded_call = ['NG', 'ND', 'NJ', 'NS', 'ZG', 'ZD', 'ZJ', 'ZS', 'FN', 'FZ',
                   'QN', 'QZ', 'GG', 'GJ', 'GB', 'GM', 'DD', 'DS', 'DB', 'DM',
                   'JG', 'JJ', 'JB', 'JM', 'SD', 'SS', 'SB', 'SM', 'BQ', 'MF',
                   'FQ', 'QF']
    replaced_call_1 = ['Z', 'Z', 'N', 'N', 'Z', 'Z', 'N', 'N', 'N', 'Z',
                       'N', 'Z', 'G', 'J', 'B', 'M', 'D', 'S', 'B', 'M',
                       'G', 'J', 'B', 'M', 'D', 'S', 'B', 'M', 'M', 'B',
                       '', '']
    # 需判断前点性别的16组可折叠代号组合
    wrong_call = ['GD', 'GS', 'DG', 'DJ', 'JD', 'JS', 'SG', 'SJ',
                  'NB', 'NM', 'ZB', 'ZM', 'BN', 'BZ', 'MN', 'MZ']
    replace_call_2 = ['D', 'S', 'G', 'J', 'D', 'S', 'G', 'J',
                      'F', 'Q', 'F', 'Q', 'S', 'D', 'M', 'D']
    replace_dic = dict(zip(folded_call + wrong_call, replaced_call_1 + replace_call_2))  # 2种拼成一个字典

    while True:
        k = 0  # 设置循环跳出判断标志
        for x, y in replace_dic.items():
            i = seq_in.find(x)
            # 只要有无效代号就进入判定及处理
            if i != -1:
                k += 1
                # 把三种处理方式通过字典与列表的分离而放到一块
                if x in folded_call:
                    seq_in = seq_in.replace(x, y)  # 可折叠的直接折叠
                elif x in wrong_call and (i == 0 or (gender_check(seq_in[i - 1]) != gender_check(seq_in[i + 1]))):
                    seq_in = seq_in.replace(x, y)  # 判定不同性别的折叠
                elif x in wrong_call and (gender_check(seq_in[i - 1]) == gender_check(seq_in[i + 1])):
                    seq_in = seq_in.replace(x, '')  # 判定不同性别的直接去掉无用代号
        if k == 0:
            break  # 一次循环检查48个全都没有匹配才能跳出

    return seq_in


def seq_check(seq_in):
    """
    代号序列压缩完毕，再检查是否有错误代号或超长
    :param seq_in:
    :return: 返回逻辑值
    """
    error_call = ['NQ', 'ZF', 'FF', 'QQ', 'GF', 'DF', 'JQ', 'SQ', 'BF', 'MQ']
    result = True
    for i in error_call:
        if i in seq_in:
            print("输入有误，还不明确该如何处理同性婚姻……")
            result = False

    if len(seq_in) > 6:
        print("这个人好像跟你没什么关系啊") # 压缩后还超过6个节点说明实在太远
        result = False
    return result


def main_cal(s):
    """
    主题计算算法，通过三层嵌套字典实现代号序列的有效遍历
    :param s: 压缩无误可计算的代号序列
    :return: 返回计算结果中文序列号
    """
    # 粗暴建立三级节点
    sunzi = {"N": "曾孙女", "Z": "曾孙", "Q": "孙媳妇", "I": "孙子"}
    sunnv = {"N": "曾外孙女", "Z": "曾外孙", "F": "孙女婿", "I": "孙女"}
    waisun = {"N": "外曾孙女", "Z": "外曾孙", "Q": "外孙媳", "I": "外孙"}
    waisunnv = {"N": "外增外孙女", "Z": "外曾外孙", "F": "外孙女婿", "I": "外孙女"}
    zhizi = {"N": "侄孙女", "Z": "侄孙", "Q": "侄媳妇", "I": "侄子"}
    zhinv = {"N": "外侄孙女", "Z": "外侄孙", "F": "侄女婿", "I": "侄女"}
    waisheng = {"N": "外甥孙女", "Z": "外甥孙", "Q": "外甥媳妇", "I": "外甥"}
    waishengnv = {"N": "外甥孙女", "Z": "外甥孙", "F": "外甥女婿", "I": "外甥女"}
    erxi = {"G": "姻侄", "D": "姻侄", "J": "姻侄女", "S": "姻侄女", "B": "亲家公", "M": "亲家母", "I": "儿媳"}
    nvxu = {"G": "姻侄", "D": "姻侄", "J": "姻侄女", "S": "姻侄女", "B": "亲家公", "M": "亲家母", "I": "女婿"}
    dimei = {"G": "姻兄弟", "D": "姻兄弟", "J": "姻姐妹", "S": "姻姐妹", "B": "姻伯父（叫叔叔就行）", "M": "姻伯母（叫阿姨就行）", "I": "弟妹"}
    saozi = {"G": "姻兄弟", "D": "姻兄弟", "J": "姻姐妹", "S": "姻姐妹", "B": "姻伯父（叫叔叔就行）", "M": "姻伯母（叫阿姨就行）", "I": "嫂子"}
    jiefu = {"G": "姻兄弟", "D": "姻兄弟", "J": "姻姐妹", "S": "姻姐妹", "B": "姻伯父（叫叔叔就行）", "M": "姻伯母（叫阿姨就行）", "I": "姐夫"}
    meifu = {"G": "姻兄弟", "D": "姻兄弟", "J": "姻姐妹", "S": "姻姐妹", "B": "姻伯父（叫叔叔就行）", "M": "姻伯母（叫阿姨就行）", "I": "妹夫"}
    xiaojiuzi = {"N": "内侄女", "Z": "内侄", "Q": "舅弟媳", "I": "小舅子"}
    dajiuzi = {"N": "内侄女", "Z": "内侄", "Q": "舅嫂", "I": "大舅子"}
    dayizi = {"N": "姨甥女", "Z": "内甥", "F": "大姨夫，跟着叫姐夫吧", "I": "大姨子"}
    xiaoyizi = {"N": "姨甥女", "Z": "内甥", "F": "小姨夫，跟着叫妹夫吧", "I": "小姨子"}
    xiaoshuzi = {"N": "侄女", "Z": "叔侄", "Q": "小婶子", "I": "小叔子"}
    dabozi = {"N": "侄女", "Z": "叔侄", "Q": "大婶子", "I": "大伯子"}
    daguzi = {"N": "姑甥女", "Z": "姑甥", "F": "大姑父，跟着叫姐夫吧", "I": "大姑子"}
    xiaoguzi = {"N": "姑甥女", "Z": "姑甥", "F": "小姑父，跟着叫妹夫吧", "I": "小姑子"}
    shufu = {"N": "堂姐妹", "Z": "堂兄弟", "Q": "婶婶", "I": "叔父"}
    bofu = {"N": "堂姐妹", "Z": "堂兄弟", "Q": "婶婶", "I": "伯父"}
    gugu = {"N": "姑表姐妹", "Z": "姑表兄弟", "F": "姑父", "I": "姑姑"}
    jiujiu = {"N": "舅表姐妹", "Z": "舅表兄弟", "Q": "舅妈", "I": "舅舅"}
    yima = {"N": "姨表姐妹", "Z": "姨表兄弟", "F": "姨父", "I": "姨妈"}
    yuefu = {"G": "伯岳父（媳妇咋叫你咋叫）", "D": "叔岳父（媳妇咋叫你咋叫）", "J": "姑岳母（媳妇咋叫你咋叫）",
             "S": "姑岳母（媳妇咋叫你咋叫）", "B": "太岳父（媳妇咋叫你咋叫）", "M": "太岳母（媳妇咋叫你咋叫）", "I": "岳父"}
    yuemu = {"G": "舅岳父（媳妇咋叫你咋叫）", "D": "舅岳父（媳妇咋叫你咋叫）", "J": "姨岳母（媳妇咋叫你咋叫）",
             "S": "姨岳母（媳妇咋叫你咋叫）", "B": "外太岳父（媳妇咋叫你咋叫）", "M": "外太岳母（媳妇咋叫你咋叫）", "I": "岳母"}
    gongong = {"G": "伯翁（老公咋叫你咋叫）", "D": "叔公（老公咋叫你咋叫）", "J": "姑婆（老公咋叫你咋叫）",
               "S": "姑婆（老公咋叫你咋叫）", "B": "祖翁（老公咋叫你咋叫）", "M": "祖婆（老公咋叫你咋叫）", "I": "公公"}
    popo = {"G": "舅公（老公咋叫你咋叫）", "D": "舅公（老公咋叫你咋叫）", "J": "姨婆（老公咋叫你咋叫）",
            "S": "姨婆（老公咋叫你咋叫）", "B": "外公", "M": "外婆", "I": "婆婆"}
    yeye = {"G": "伯祖父", "D": "叔祖父", "J": "姑奶奶", "S": "姑奶奶", "B": "曾祖父", "M": "曾祖母", "I": "爷爷"}
    nainai = {"G": "舅公", "D": "舅公", "J": "姨奶奶", "S": "姨奶奶", "B": "曾外祖父", "M": "曾外祖母", "I": "奶奶"}
    laoye = {"G": "伯外祖父", "D": "伯外祖父", "J": "姑姥姥", "S": "姑姥姥", "B": "外曾祖父", "M": "外曾祖母", "I": "姥爷"}
    laolao = {"G": "外舅公", "D": "外舅公", "J": "姨姥姥", "S": "姨姥姥", "B": "外曾外祖父", "M": "外曾外祖母", "I": "姥姥"}
    # 建立二级节点
    nver = {'N': waisunnv, 'Z': waisun, 'F': nvxu, 'I': "女儿"}
    erzi = {'N': sunnv, 'Z': sunzi, 'Q': erxi, 'I': "儿子"}
    zhangfu = {'G': dabozi, 'D': xiaoshuzi, 'J': daguzi, 'S': xiaoguzi, 'B': gongong, 'M': popo, 'I': "丈夫"}
    qizi = {'G': dajiuzi, 'D': xiaojiuzi, 'J': dayizi, 'S': xiaoyizi, 'B': yuefu, 'M': yuemu, 'I': "妻子"}
    gege = {'N': zhinv, 'Z': zhizi, 'Q': saozi, 'I': "哥哥"}
    didi = {'N': zhinv, 'Z': zhizi, 'Q': dimei, 'I': "弟弟"}
    jiejie = {'N': waishengnv, 'Z': waisheng, 'F': jiefu, 'I': "姐姐"}
    meimei = {'N': waishengnv, 'Z': waisheng, 'F': meifu, 'I': "妹妹"}
    baba = {'G': bofu, 'D': shufu, 'J': gugu, 'S': gugu, 'B': yeye, 'M': nainai, 'I': "爸爸"}
    mama = {'G': jiujiu, 'D': jiujiu, 'J': yima, 'S': yima, 'B': laoye, 'M': laolao, 'I': "妈妈"}
    # 建立一级节点
    The_dict = dict(zip(code_list, [nver, erzi, zhangfu, qizi, gege, didi, jiejie, meimei, baba, mama]))
    L = len(s)
    a = '这位是你的'

    def test1(seq_in):
        l = len(seq_in)
        b = ''
        if l == 1:
            b = The_dict[seq_in[0]]['I']
        elif l == 2:
            b = The_dict[seq_in[0]][seq_in[1]]['I']
        elif l == 3:
            b = The_dict[seq_in[0]][seq_in[1]][seq_in[2]]
        return b

    if L <4:
        a += test1(s)
    else:
        a += test1(s[:3])+'的' +test1(s[3:])+ "，隔得好像有点远"


    return a


def menu():
    """
    初始界面打印程序
    :return: 无
    """
    print("#" * 70, end='\n\n')
    print("     欢迎使用极其简易版亲戚计算器 Kinship_Calculation 0.1      \n")
    print("#" * 70, end='\n\n')
    print("目前功能还很低级，只能计算以下十种关系的组合：\n"
          "女儿  儿子  丈夫  妻子  哥哥  弟弟  姐姐  妹妹  爸爸  妈妈\n"
          "请用“的”连接你想计算的亲戚关系，如爸爸的妈妈的姐姐，按Enter查看结果，按Q退出：\n")


def example():
    """
    举例程序，show一下典型的输入输出，省去打字
    :return: 无
    """
    exa = ["爸爸的妈妈的姐姐的老公的弟弟的老婆",
           "老婆的哥哥的妹妹的姐姐的爸爸的老婆的爸爸的姐姐的老公",
           "哥哥的儿子的姐姐的老公的爸爸的妹妹的老公",
           "哥哥的弟弟的姐姐的妹妹的爸爸的妈妈的姐姐的妈妈的女儿的老公的姐姐"]
    for i in exa:
        j = input_handle(i)
        j = seq_simplify(j)
        if seq_check(j):
            print(i)
            print(main_cal(j))
    print("………………\n举例完毕，请输入你想计算的关系：")


if __name__ == '__main__':
    menu()
    # 主函数循环体，无主动清屏功能，待改善。 i = os.system('cls')
    while True:
        the_str = input()
        if the_str.upper() == "Q": # 见Q就退出
            print("退出计算器")
            break
        elif the_str == 'show':  # 加一个自动举例的环节便于展示
            example()
        elif input_check(the_str): # 主体计算判别部分
            seq = input_handle(the_str)
            seq = seq_simplify(seq)
            if seq_check(seq):
                print(main_cal(seq))
        else:
            print("输入有问题呐，人家又没有搞自然语言处理，很笨的只会按照示例计算，请重新输入：")

    os.system('pause') # 系统暂停，防闪退